#pragma once
#include<iostream>
#include"../Comm/Log.hpp"
#include"../Comm/Util.hpp"
#include<string>
#include<vector>
#include"Oj_Model1.hpp"
#include"Oj_View.hpp"
#include<jsoncpp/json/json.h>
#include<mutex>
#include<fstream>
#include<assert.h>
#include"../Comm/httplib.h"
#include<algorithm>

//核心控制模块
namespace control_ns
{
    using namespace model_ns;
    using namespace httplib;
    using namespace view_ns;
    using namespace util_ns;
    using namespace httplib;

    class HostMachine
    {
        public:
            std::string _ip;//编译运行服务主机的ip地址
            int _port;//端口号
            uint64_t _load;//负载情况
            std::mutex* _mutex;
        public:
            HostMachine():_load(0),_mutex(nullptr){}
            ~HostMachine(){}
        public:
            void IncLoad()
            {
                _mutex->lock();
                ++_load;
                _mutex->unlock();
            }

            void DecLoad()
            {
                _mutex->lock();
                --_load;
                _mutex->unlock();
            }

            void ReLoad()
            {
                _mutex->lock();
                _load = 0;
                _mutex->unlock();
            }

            int GetLoad()
            {
                uint64_t load = 0;
                _mutex->lock();
                load = _load;
                _mutex->unlock();

                return load;
            }
    };

    static const std::string hostpath = "./Config/compile_service_host.conf";
    class LoadBalance
    {
        private:
            std::vector<HostMachine> _host;//所有的主机

            std::vector<int> _online;//所有在线的主机
            std::vector<int> _offline;//所有离线的主机
            std::mutex _mut;//锁，保护当前类中的数据安全
        public:
            LoadBalance()
            {
                assert(LoadConfig());
            }
            ~LoadBalance()
            {
            
            }
        public:
            bool LoadConfig()
            {
                //0.导入配置文件进入内存
                std::ifstream ifs(hostpath);

                //1.判断是否正确打开文件
                if(!ifs.is_open())
                {
                    LOG(FATAL) << "编译服务配置打开失败,请检查路径是否设置正确..."  << "\n";
                    return false;
                }

                //2.开始按行读取内容,同时配置主机
                std::string line;

                while(std::getline(ifs,line))
                {
                    std::vector<std::string> res;
                    Spilt_Util::Split(line,&res,":");

                    if(res.size() != 2)
                    {
                        LOG(WARNNING) << "此行配置出错..." << "\n";
                        continue;
                    }
                    HostMachine machine;
                    machine._ip = res[0];
                    machine._port = atoi(res[1].c_str());
                    machine._mutex = new std::mutex;

                    //将主机添加到_host中
                    _online.emplace_back(_host.size());
                    // std::cout << "_onlinesize" << _online.size() << std::endl;
                    _host.emplace_back(machine);
                }

                LOG(INFO) << "导入主机配置成功..."  << "\n";
                return true;
            }

            //id,hm都是输出型参数
            bool SmartChoice(int* id,HostMachine** hm)
            {
                _mut.lock();//加锁保护在线主机、离线主机列表等公共数据

                int onlinenum = _online.size();

                if(onlinenum == 0)
                {
                    _mut.unlock();
                    LOG(FATAL) << "所有后端编译主机的已经离线,服务无法继续..." << "\n";
                    return false;
                }

                //负载均衡算法
                //1.随机数+hash
                //2.轮询+hash->我们选择这种
                (*id) = _online[0];
                (*hm) = &_host[*id];
                uint64_t min_load = _host[_online[0]].GetLoad();

                for(int i = 1;i < _online.size();++i)
                {
                    uint64_t cur_load  = _host[_online[i]].GetLoad();
                    if(cur_load < min_load)
                    {
                        min_load = cur_load;
                        (*id) = _online[i];
                        (*hm) = &_host[*id];
                    }
                }

                _mut.unlock();
                return true;
            }

            void OfflineHost(int which)
            {
                _mut.lock();
                for(auto iter = _online.begin();iter != _online.end();++iter)
                {
                    if((*iter) == which)
                    {
                        _host[which].ReLoad();
                        _online.erase(iter);
                        _offline.emplace_back(which);
                        break;
                    }
                }
                _mut.unlock();
            }

            void OnlineHost()
            {
                _mut.lock();
                //将所有的离线主机中的全加入在线主机中
                _online.insert(_online.end(),_offline.begin(),_offline.end());
                _offline.erase(_offline.begin(),_offline.end());
                _mut.unlock();

                LOG(INFO) << "所有主机都上线了" << "\n";
            }

 
            void ShowHostlist()
            {
                _mut.lock();
                //打印在线主机列表
                std::cout << "在线主机列表: ";

                for(int i = 0;i < _online.size();++i)
                {
                    std::cout << _online[i] << " ";
                }

                std::cout << "\n";

                std::cout << "离线主机列表: ";

                for(int i = 0;i < _offline.size();++i)
                {
                    std::cout << _offline[i] << " ";
                }

                std::cout << "\n";
                _mut.unlock();
            }
    };


    class Control
    {
        private:
            Model _model;
            View _view;
            LoadBalance _LoadBalance;
        public:
            Control(){}
            ~Control(){}
        public:

            void RecoveryMachine()
            {
                _LoadBalance.OnlineHost();
            }

            //返回给外层回调一个包含所有题目的html文件,要经过网页渲染，View中的功能
            bool AllQuestions(std::string* html)
            {
                bool ret;
                //将所有的题目的题目列表信息放入我们的question_v中
                std::vector<Question*> question_v;
                if(_model.GetAllQuestions(&question_v))
                {
                    sort(question_v.begin(),question_v.end(),[](Question* a,Question*b)->bool{
                        return atoi(a->number.c_str()) < atoi(b->number.c_str());
                    });//升序排序
                    _view.AllRenderHtml(question_v,html);
                    ret = true;
                }

                else
                {
                    *html = "获取所有题目列表失败";
                    ret = false;
                }

                return true;
            }

            bool OneQuestion(const std::string& number,std::string* html)
            {
                Question* question;
                bool ret;
                if(_model.GetOneQuestion(number,&question))
                {
                    LOG(INFO) << "获取题目成功,number is " << number << "\n";
                    _view.OneRenderHtml(question,html);
                }

                else
                {
                    LOG(INFO) << "获取题目失败,number is " << number << "\n";
                    ret = false;
                    *html = "获取题目失败,number is " + number;
                }

                
                return ret;
            }

            void  Judge(const std::string& number,const std::string& in_json,std::string * out_json)
            {
                //0.根据题目编号，拿到题目细节
                Question* question;
                _model.GetOneQuestion(number,&question);

                //1.1将用户提交的in_json,解析出来 -- 反序列化
                Json::Value in_Value;
                Json::Reader reader;
                reader.parse(in_json,in_Value);

                //1.1拿到结构化数据
                std::string submit_code = in_Value["code"].asCString();//用户提交的代码
                std::string input = in_Value["input"].asCString();

                //2.构建给后端编译服务的请求json串
                //2.1将拿到用户提交的代码和测试用例拼接在一起
                std::string code = submit_code+"\n"+ question->tail;
                Json::Value out_Value;
                
                //2.2构建json数据
                out_Value["code"] =  code;
                out_Value["input"] =  input;
                out_Value["cpu_limit"] = question->cpu_limit;
                out_Value["mem_limit"] = question->mem_limit;
                Json::FastWriter writer;
                
                //2.3反序列化，得到发送给compile_run服务的json串
                std::string compile_json = writer.write(out_Value);

                //3.负载均衡式的选择主机
                while(true)
                {
                    int id = 0;
                    HostMachine* host;
                    //3.1负载均衡式的获取主机
                    if(!_LoadBalance.SmartChoice(&id,&host))
                    {
                        LOG(ERROR) << "主机都未上线,id is " << id << "\n";
                        break;
                    }

                    //走到这里，一定获取到了一个在线主机
                    // 开始发送http post请求
                    //3.2创建httplib client
                    Client client(host->_ip,host->_port);
                    host->IncLoad();

                    //设置client的超时时间
                    client.set_read_timeout(std::chrono::seconds(20));//20s超时
                    client.set_connection_timeout(std::chrono::seconds(20));
                    LOG(INFO) << "当前请求的主机,id: " << id << " 主机详情: " << host->_ip <<  ":" << host->_port << "\n";
                    //发送post请求向目标主机
                    auto res = client.Post("/Compile_Run",compile_json,"application/json;charset=utf-8");
                    
                    if(res)
                    {
                        if(res->status == 200)//响应成功
                        {
                            host->DecLoad();
                            (*out_json) = res->body;
                            LOG(INFO) << "编译运行成功" << "\n";
                            break;
                        }

                        host->DecLoad();
                    }

                    else
                    {
                        LOG(ERROR) << "请求编译运行服务失败,可能已经离线当前主机id: " << id << " 主机详情: " << host->_ip <<  ":" << host->_port << "\n";
                        _LoadBalance.OfflineHost(id);
                        _LoadBalance.ShowHostlist();
                    }
                    
                
                }
            }

    };
}
